#!/bin/bash
# TCP拥塞算法切换失败修复脚本
# 适用于Linux系统中TCP拥塞控制算法切换异常导致带宽利用率低的情况
# 知识图谱关联配置 - 与故障节点ID对应
FIX_CODE_ID="TCP-001"  # 对应知识图谱中"TCP拥塞算法切换失败（带宽利用率低）"故障ID


# 配置参数
LOG_FILE="/var/log/tcp_congestion_fix.log"
PREFERRED_ALGORITHMS=("bbr" "cubic" "reno")  # 优先使用的拥塞算法（按优先级排序）
BANDWIDTH_CHECK_INTERVAL=10  # 带宽检查间隔（秒）
MIN_BANDWIDTH_USAGE=30       # 最低带宽利用率阈值（%），低于此值视为异常
CHECK_COUNT=2                # 连续检查次数

# 初始化日志
log() {
    local timestamp=$(date "+%Y-%m-%d %H:%M:%S")
    echo "[$timestamp] $1" >> "$LOG_FILE"
    echo "[$timestamp] $1"  # 同时输出到控制台
}

# 检查系统是否支持TCP拥塞算法管理
check_tcp_support() {
    if [ -f "/proc/sys/net/ipv4/tcp_congestion_control" ]; then
        return 0
    else
        log "系统不支持TCP拥塞控制算法配置"
        return 1
    fi
}

# 获取当前使用的TCP拥塞算法
get_current_algorithm() {
    cat /proc/sys/net/ipv4/tcp_congestion_control
}

# 获取已加载的TCP拥塞算法模块
get_loaded_algorithms() {
    sysctl net.ipv4.tcp_available_congestion_control | awk '{print $3}'
}

# 检查带宽利用率
check_bandwidth_usage() {
    # 获取网络接口（排除lo）
    local interface=$(ip -br link show | grep -v LOOPBACK | awk '{print $1}' | head -n 1)
    if [ -z "$interface" ]; then
        log "无法识别网络接口"
        return 1
    fi

    # 分别读取收发字节数（修复换行问题）
    local initial_rx=$(cat /sys/class/net/"$interface"/statistics/rx_bytes)
    local initial_tx=$(cat /sys/class/net/"$interface"/statistics/tx_bytes)
    local initial_total=$((initial_rx + initial_tx))

    # 等待检查间隔
    sleep $BANDWIDTH_CHECK_INTERVAL

    # 分别读取间隔后收发字节数
    local final_rx=$(cat /sys/class/net/"$interface"/statistics/rx_bytes)
    local final_tx=$(cat /sys/class/net/"$interface"/statistics/tx_bytes)
    local final_total=$((final_rx + final_tx))

    # 计算使用的带宽（字节/秒）
    local used_bytes=$((final_total - initial_total))
    local used_bps=$((used_bytes * 8 / BANDWIDTH_CHECK_INTERVAL))

    # 获取接口最大带宽（修复单位处理问题）
    local max_bandwidth_str=$(ethtool "$interface" | grep "Speed:" | awk '{print $2}')
    # 提取数字部分（移除所有非数字字符）
    local max_bandwidth=$(echo "$max_bandwidth_str" | sed 's/[^0-9]//g')
    # 提取单位（Mb/Gb）
    local bandwidth_unit=$(echo "$max_bandwidth_str" | sed 's/[0-9]//g')

    if [ -z "$max_bandwidth" ] || [ "$max_bandwidth_str" = "Unknown!" ]; then
        log "无法获取网络接口$interface的最大带宽，使用默认值100Mbps"
        max_bandwidth=100
        bandwidth_unit="Mb"
    fi

    # 根据单位转换为Mbps（1Gb = 1000Mb）
    if [ "$bandwidth_unit" = "Gb" ]; then
        max_bandwidth=$((max_bandwidth * 1000))
    fi

    # 计算带宽利用率（%）
    local max_bps=$((max_bandwidth * 1000000))  # 转换为bps
    local usage_percent=$((used_bps * 100 / max_bps))

    log "网络接口$interface带宽使用情况: 已使用$usage_percent% (${used_bps}bps / ${max_bps}bps)"
    echo $usage_percent
}

# 检查是否存在带宽利用率低的问题
detect_low_bandwidth() {
    local low_usage_count=0

    for ((i=0; i<CHECK_COUNT; i++)); do
        # 修复：捕获带宽利用率，确保为数字
        local usage=$(check_bandwidth_usage)
        # 验证usage是否为有效数字
        if ! [[ "$usage" =~ ^[0-9]+$ ]]; then
            log "警告：获取带宽利用率失败，返回值为无效数字: $usage"
            # 无效值时视为异常（累计低利用率计数）
            ((low_usage_count++))
            continue
        fi
        
        # 修复：使用双引号包裹变量，避免空格导致的参数解析错误
        if [ "$usage" -lt "$MIN_BANDWIDTH_USAGE" ]; then
            ((low_usage_count++))
        fi
        if [ $i -lt $((CHECK_COUNT - 1)) ]; then
            sleep $BANDWIDTH_CHECK_INTERVAL
        fi
    done

    if [ "$low_usage_count" -ge "$CHECK_COUNT" ]; then
        log "连续$CHECK_COUNT次检测到带宽利用率低于阈值$MIN_BANDWIDTH_USAGE%"
        return 0  # 存在带宽利用率低的问题
    else
        log "带宽利用率在正常范围内"
        return 1  # 带宽使用正常
    fi
}
# 重新加载TCP拥塞控制算法模块
reload_congestion_module() {
    local current_algorithm=$(get_current_algorithm)
    log "当前使用的TCP拥塞算法: $current_algorithm"

    # 尝试卸载并重新加载当前算法模块
    if lsmod | grep -q "$current_algorithm"; then
        log "尝试卸载$current_algorithm模块"
        if sudo rmmod "$current_algorithm"; then
            log "$current_algorithm模块卸载成功"
            
            log "尝试重新加载$current_algorithm模块"
            if sudo modprobe "$current_algorithm"; then
                log "$current_algorithm模块重新加载成功"
            else
                log "$current_algorithm模块重新加载失败，尝试加载备用算法"
                return 1
            fi
        else
            log "$current_algorithm模块卸载失败，尝试直接加载"
            if sudo modprobe "$current_algorithm"; then
                log "$current_algorithm模块加载成功"
            else
                log "$current_algorithm模块加载失败，尝试备用算法"
                return 1
            fi
        fi
    else
        log "$current_algorithm模块未直接加载，尝试重新加载核心TCP模块"
        sudo modprobe -r tcp_cubic 2>/dev/null
        sudo modprobe -r tcp_reno 2>/dev/null
        sudo modprobe tcp_cubic
        sudo modprobe tcp_reno
    fi

    return 0
}

# 切换到更优的TCP拥塞算法
switch_algorithm() {
    local current_algorithm=$(get_current_algorithm)
    local loaded_algorithms=$(get_loaded_algorithms)
    
    log "已加载的TCP拥塞算法: $loaded_algorithms"

    # 查找优先算法列表中可用的其他算法
    for algo in "${PREFERRED_ALGORITHMS[@]}"; do
        if [ "$algo" != "$current_algorithm" ] && echo "$loaded_algorithms" | grep -q "$algo"; then
            log "尝试切换到$algo算法"
            if sudo sysctl -w net.ipv4.tcp_congestion_control="$algo"; then
                log "成功切换到$algo算法"
                return 0
            else
                log "切换到$algo算法失败"
            fi
        fi
    done

    # 如果没有可用的其他算法，尝试加载首选算法
    for algo in "${PREFERRED_ALGORITHMS[@]}"; do
        if [ "$algo" != "$current_algorithm" ]; then
            log "尝试加载并切换到$algo算法"
            if sudo modprobe "$algo" && sudo sysctl -w net.ipv4.tcp_congestion_control="$algo"; then
                log "成功加载并切换到$algo算法"
                return 0
            else
                log "加载并切换到$algo算法失败"
            fi
        fi
    done

    log "无法切换到其他拥塞算法"
    return 1
}

# 检查IP路由状态
check_ip_route() {
    log "检查IP路由状态..."
    local default_route=$(ip route show default)
    if [ -z "$default_route" ]; then
        log "警告: 未找到默认路由"
        return 1
    else
        log "默认路由正常: $default_route"
        return 0
    fi
}

# 主函数
main() {
    log "===== TCP拥塞算法修复脚本启动 ====="

    # 检查系统是否支持TCP拥塞控制
    if ! check_tcp_support; then
        log "不支持TCP拥塞控制配置，退出脚本"
        return 1
    fi

    # 检查IP路由状态
    check_ip_route

    # 检测带宽利用率
    if ! detect_low_bandwidth; then
        log "带宽利用率正常，无需修复"
        return 0
    fi

    # 重新加载当前拥塞算法模块
    log "尝试重新加载TCP拥塞算法模块..."
    if reload_congestion_module; then
        # 检查修复后带宽情况
        sleep $BANDWIDTH_CHECK_INTERVAL
        if detect_low_bandwidth; then
            log "重新加载模块后带宽利用率仍然较低，尝试切换算法"
            switch_algorithm
            
            # 再次检查
            sleep $BANDWIDTH_CHECK_INTERVAL
            if detect_low_bandwidth; then
                log "切换算法后带宽利用率仍然较低，建议人工干预"
            else
                log "切换算法后带宽利用率恢复正常"
            fi
        else
            log "重新加载模块后带宽利用率恢复正常"
        fi
    else
        # 加载当前模块失败，尝试切换算法
        log "尝试切换到其他TCP拥塞算法..."
        switch_algorithm
        
        # 检查修复后带宽情况
        sleep $BANDWIDTH_CHECK_INTERVAL
        if detect_low_bandwidth; then
            log "切换算法后带宽利用率仍然较低，建议人工干预"
        else
            log "切换算法后带宽利用率恢复正常"
        fi
    fi

    log "===== TCP拥塞算法修复脚本执行完毕 ====="
}

# 启动主函数
main
